import sys
import asyncio
import os
import glob
from PySide6.QtWidgets import QApplication, QMessageBox
from PySide6.QtCore import QThread, QObject, Signal, Slot, QMetaObject, Qt, QTimer, Q_ARG

from app_ui import MainWindow, DeviceControlWidget
from ble_manager import BLEManager
from protocol_manager import ProtocolManager

class Worker(QObject):
    """
    工作线程, 用于在后台执行异步的BLE操作, 避免阻塞UI.
    """
    def __init__(self, ble_manager, parent=None):
        super().__init__(parent)
        self.ble_manager = ble_manager
        self.loop = None
        self.timer = None

    @Slot()
    def setup(self):
        """
        在工作线程中设置 asyncio 事件循环和定时器.
        """
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)

        # 设置一个定时器来驱动 asyncio 事件循环
        self.timer = QTimer(self)
        self.timer.setInterval(10)  # 避免CPU占用过高
        self.timer.timeout.connect(self.run_once)
        self.timer.start()

    def run_once(self):
        """运行事件循环的一次迭代."""
        self.loop.call_soon(self.loop.stop)
        self.loop.run_forever()

    def _create_task(self, coro):
        """此方法从槽函数调用, 该槽在工作线程中执行, 因此直接创建任务是安全的."""
        self.loop.create_task(coro)

    @Slot()
    def start_scan(self):
        self._create_task(self.ble_manager.start_scan())

    @Slot()
    def stop_scan(self):
        self._create_task(self.ble_manager.stop_scan())

    @Slot(str)
    def connect(self, address):
        self._create_task(self.ble_manager.connect_to_device(address))
    
    @Slot()
    def disconnect(self):
        self._create_task(self.ble_manager.disconnect_device())
    
    @Slot(str)
    def read_characteristic(self, char_uuid):
        self._create_task(self.ble_manager.read_characteristic(char_uuid))

    @Slot(str, object)
    def write_characteristic(self, char_uuid, data):
        print(f"Worker.write_characteristic 被调用: UUID={char_uuid}, 数据={data.hex()}")
        self._create_task(self.ble_manager.write_characteristic(char_uuid, data))
        
    @Slot(str, str)
    def write_characteristic_hex(self, char_uuid, data_hex):
        """接收十六进制字符串而非字节对象，解决跨线程传递问题"""
        print(f"Worker.write_characteristic_hex 被调用: UUID={char_uuid}, 数据(hex)={data_hex}")
        try:
            data = bytes.fromhex(data_hex)
            print(f"十六进制字符串转换为字节: {data.hex()}, 长度: {len(data)}")
            self._create_task(self.ble_manager.write_characteristic(char_uuid, data))
        except Exception as e:
            print(f"十六进制字符串转换出错: {e}")

    @Slot(str)
    def start_notifications(self, char_uuid):
        self._create_task(self.ble_manager.start_notifications(char_uuid))

    @Slot(str)
    def stop_notifications(self, char_uuid):
        self._create_task(self.ble_manager.stop_notifications(char_uuid))

    @Slot()
    def stop(self):
        """停止定时器并清理循环."""
        if self.timer:
            self.timer.stop()
        if self.loop:
            # 清理所有正在运行的任务
            tasks = asyncio.all_tasks(loop=self.loop)
            for task in tasks:
                task.cancel()
            # 收集任务以让它们完成取消
            group = asyncio.gather(*tasks, return_exceptions=True)
            self.loop.run_until_complete(group)
            self.loop.close()


class AppController:
    """
    主控制器, 负责连接UI和业务逻辑.
    """
    def __init__(self):
        self.app = QApplication(sys.argv)
        self.main_window = MainWindow()
        self.ble_manager = BLEManager()
        self.protocol_manager = ProtocolManager()  # 添加协议管理器

        # 设置工作线程来处理bleak的异步IO
        self.thread = QThread()
        self.worker = Worker(self.ble_manager)
        self.worker.moveToThread(self.thread)
        
        # 在工作线程启动后进行设置
        self.thread.started.connect(self.worker.setup)

        # 在程序退出时清理
        self.app.aboutToQuit.connect(self.worker.stop)
        self.app.aboutToQuit.connect(self.thread.quit)
        self.app.aboutToQuit.connect(self.thread.wait)

        self.thread.start()

        self.connect_signals()

    def connect_signals(self):
        # 1. UI -> Worker (跨线程调用)
        self.main_window.scan_button.clicked.connect(self.toggle_scan)
        
        # 2. BLEManager -> UI (从工作线程发射信号到主线程)
        self.ble_manager.device_found_signal.connect(self.add_device)
        self.ble_manager.connection_status_signal.connect(self.main_window.update_connection_status)
        self.ble_manager.services_resolved_signal.connect(self.main_window.display_services)
        self.ble_manager.advertisement_updated_signal.connect(self.main_window.update_advertisement_data)
        self.ble_manager.characteristic_read_signal.connect(self.main_window.update_characteristic_value)
        self.ble_manager.notification_received_signal.connect(self.main_window.update_characteristic_value)
        
        # BLEManager -> ProtocolManager
        self.ble_manager.protocol_data_updated_signal.connect(self.protocol_manager.update_text_display)

        # 3. UI -> Worker (新增的特征操作)
        self.main_window.read_characteristic_requested.connect(self.worker.read_characteristic)
        self.main_window.write_characteristic_requested.connect(self.worker.write_characteristic)
        self.main_window.start_notification_requested.connect(self.worker.start_notifications)
        self.main_window.stop_notification_requested.connect(self.worker.stop_notifications)
        
        # 4. 协议测试相关信号
        self.main_window.refresh_protocol_button.clicked.connect(self.load_protocol_files)
        self.main_window.protocol_selected.connect(self.on_protocol_selected)
        self.main_window.protocol_save_requested.connect(self.on_protocol_save)
        self.main_window.protocol_new_requested.connect(self.on_protocol_new)
        self.main_window.add_component_requested.connect(self.on_add_component)
        self.main_window.lock_all_components_requested.connect(self.on_lock_all_components)
        
        # 协议管理器 -> UI
        self.protocol_manager.component_action.connect(self.on_component_action)
        
        # 初始加载协议文件
        self.load_protocol_files()
        
    def on_protocol_selected(self, protocol_file):
        """处理协议文件选择"""
        # 获取当前选择的协议文件完整路径
        current_index = self.main_window.protocol_combo.currentIndex()
        if current_index >= 0:
            full_path = self.main_window.protocol_combo.itemData(current_index)
            if full_path and full_path != "无可用协议文件":
                print(f"已选择协议文件: {full_path}")
                # 加载协议文件
                self.protocol_manager.load_protocol(full_path, self.main_window.protocol_content_area)
            else:
                print(f"无法加载协议文件: {protocol_file}")
    
    def on_protocol_save(self):
        """保存协议配置"""
        self.protocol_manager.save_protocol()
    
    def on_protocol_new(self, name, path):
        """创建新协议"""
        # 确保路径在protocols文件夹中
        if not path.startswith("protocols/") and not path.startswith("protocols\\"):
            path = os.path.join("protocols", os.path.basename(path))
            
        if self.protocol_manager.create_new_protocol(name, path):
            # 重新加载协议文件列表
            self.load_protocol_files()
            # 选择新创建的协议
            for i in range(self.main_window.protocol_combo.count()):
                if self.main_window.protocol_combo.itemText(i) == os.path.basename(path):
                    self.main_window.protocol_combo.setCurrentIndex(i)
                    break
    
    def on_add_component(self, comp_type, comp_info):
        """添加组件"""
        pos = None  # 可以在这里添加位置选择
        component = self.protocol_manager.add_component(
            comp_type, 
            comp_info["id"], 
            self.main_window.protocol_content_area,
            pos,
            comp_info  # 传递完整的组件信息
        )
        
        # 根据组件类型设置特定属性
        if component:
            if comp_type == 'button' and "text" in comp_info:
                # 设置按钮文本
                component.button.setText(comp_info["text"])
            elif comp_type == 'text_display' and "text" in comp_info:
                # 设置文本内容
                component.set_text(comp_info["text"])
            elif comp_type == 'slider':
                # 设置滑动条参数
                if "label" in comp_info:
                    component.label.setText(comp_info["label"])
                if "unit" in comp_info:
                    component.unit = comp_info["unit"]
                    value = component.get_value()
                    value_text = f"{value}{' ' + comp_info['unit'] if comp_info['unit'] else ''}"
                    component.value_label.setText(value_text)
    
    def on_lock_all_components(self, lock):
        """锁定/解锁所有组件"""
        for component in self.protocol_manager.components.values():
            component.set_locked(lock)
        # 保存状态
        self.protocol_manager.save_protocol()
    
    def on_component_action(self, comp_id, action_type, action_data):
        """处理组件操作"""
        # 添加日志打印
        print(f"组件操作触发: ID={comp_id}, 类型={action_type}, 数据={action_data}")
        
        # 检查是否已连接蓝牙设备，未连接时不发送数据也不弹出警告
        if not self.ble_manager.connected_client:
            # 未连接蓝牙设备时，不执行任何操作，静默返回
            print("蓝牙设备未连接，无法发送数据")
            return
        
        if action_type == "button_click":
            if action_data.get("type") == "write":
                char_uuid = action_data.get("characteristic")
                data_str = action_data.get("data", "")
                print(f"准备发送数据: UUID={char_uuid}, 数据={data_str}")
                
                try:
                    # 使用新的write_characteristic_hex方法，避免跨线程传递复杂对象
                    QMetaObject.invokeMethod(
                        self.worker, 
                        'write_characteristic_hex', 
                        Qt.ConnectionType.QueuedConnection,
                        Q_ARG(str, char_uuid),
                        Q_ARG(str, data_str)
                    )
                    print(f"已请求写入特征: UUID={char_uuid}")
                    
                    # 转换为字节后更新UI
                    try:
                        data = bytes.fromhex(data_str)
                        # 在服务与特征数据栏中显示发送的数据
                        self.main_window.update_sent_data(char_uuid, data)
                        print(f"已更新UI显示")
                    except Exception as e:
                        print(f"UI更新时转换数据出错: {str(e)}")
                except Exception as e:
                    print(f"处理数据时出错: {str(e)}")
        
        elif action_type == "slider_change":
            if action_data.get("type") == "write":
                char_uuid = action_data.get("characteristic")
                data_str = action_data.get("data", "")
                print(f"准备发送滑动条数据: UUID={char_uuid}, 数据={data_str}")
                
                try:
                    # 使用新的write_characteristic_hex方法，避免跨线程传递复杂对象
                    QMetaObject.invokeMethod(
                        self.worker, 
                        'write_characteristic_hex', 
                        Qt.ConnectionType.QueuedConnection,
                        Q_ARG(str, char_uuid),
                        Q_ARG(str, data_str)
                    )
                    print(f"已请求写入特征(滑动条): UUID={char_uuid}")
                    
                    # 转换为字节后更新UI
                    try:
                        data = bytes.fromhex(data_str)
                        # 在服务与特征数据栏中显示发送的数据
                        self.main_window.update_sent_data(char_uuid, data)
                        print(f"已更新UI显示(滑动条)")
                    except Exception as e:
                        print(f"UI更新时转换数据出错(滑动条): {str(e)}")
                except Exception as e:
                    print(f"处理滑动条数据时出错: {str(e)}")
    
    def load_protocol_files(self):
        """加载protocols文件夹下的协议配置文件（.json）"""
        self.main_window.protocol_combo.clear()
        
        # 确保protocols文件夹存在
        os.makedirs("protocols", exist_ok=True)
        
        # 获取protocols文件夹下所有的json文件
        protocol_files = glob.glob("protocols/*.json")
        
        if protocol_files:
            for file in protocol_files:
                # 仅显示文件名，不显示路径
                self.main_window.protocol_combo.addItem(os.path.basename(file), file)
        else:
            self.main_window.protocol_combo.addItem("无可用协议文件")

    def toggle_scan(self):
        """
        切换扫描状态.
        """
        if not self.ble_manager.is_scanning:
            self.main_window.scan_button.setText("停止扫描")
            self.main_window.device_tree.clear()
            # 使用 QMetaObject.invokeMethod 来确保在正确的线程上调用槽
            QMetaObject.invokeMethod(self.worker, 'start_scan', Qt.ConnectionType.QueuedConnection)
        else:
            self.main_window.scan_button.setText("扫描设备")
            QMetaObject.invokeMethod(self.worker, 'stop_scan', Qt.ConnectionType.QueuedConnection)

    @Slot(object, object)
    def add_device(self, device, advertisement_data):
        """
        在设备树中添加一个设备, 并连接其控制按钮的信号.
        """
        item, control_widget = self.main_window.add_device_to_tree(device, advertisement_data)
        if isinstance(control_widget, DeviceControlWidget):
            # 将按钮点击信号连接到Worker的槽
            control_widget.connect_clicked.connect(self.worker.connect)
            control_widget.disconnect_clicked.connect(self.worker.disconnect)

    def run(self):
        self.main_window.show()
        sys.exit(self.app.exec())

if __name__ == "__main__":
    controller = AppController()
    controller.run() 